package com.sunseaypw.springmvc;

import java.io.File;
import java.util.HashMap;
import java.util.HashSet;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

import com.mitchellbosecke.pebble.PebbleEngine;
import com.mitchellbosecke.pebble.loader.ServletLoader;
import com.mitchellbosecke.pebble.spring.extension.SpringExtension;
import com.mitchellbosecke.pebble.spring.servlet.PebbleViewResolver;
import com.sunseaypw.springmvc.web.ChatHandler;
import com.sunseaypw.springmvc.web.ChatHandshakeInterceptor;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import org.apache.catalina.Context;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.webresources.DirResourceSet;
import org.apache.catalina.webresources.StandardRoot;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;


@EnableMBeanExport
@EnableScheduling //切记默认与@EnableWebSocket  冲突使用线程池，好像是websocket aop了默认的线程池，造成sched异常，可以附加一个新池
@EnableWebSocket //才能生效 websocket handler
@Configuration
//等同于 xml中的<context:component-scan />
@ComponentScan(basePackages ={ "com.sunseaypw.springmvc","com.sunseaypw.springmvc" } )
@EnableWebMvc
//等同于 xml中的<tx:annotation-driven />  
@EnableTransactionManagement
@PropertySource("classpath:/jdbc.properties")
public class AppConfig{
    public static void main(String[] args) throws Exception {
		Tomcat tomcat = new Tomcat();
		tomcat.setPort(Integer.getInteger("port", 9999));
		tomcat.getConnector();
		Context ctx = tomcat.addWebapp("", new File("web-springmvc/src/main/webapp").getAbsolutePath());
		WebResourceRoot resources = new StandardRoot(ctx);
		resources.addPreResources(
				new DirResourceSet(resources, "/WEB-INF/classes", new File("web-springmvc/target/classes").getAbsolutePath(), "/"));
		ctx.setResources(resources);
		
		tomcat.start();
		tomcat.getServer().await();
	}
	
	@Component
	public static class SchedDemo{
		@Scheduled(initialDelay = 2_000, fixedRate = 3_000)
		void scheduleA(){
			System.out.println("schecduleAAAA THREAD NAME:"+Thread.currentThread().getName());
		}
	}
	
	@Component
	@ManagedResource(objectName = "wwww:kjkj=bbbb",description = "zhejiu时一个验证")
	public static class MyMBean{
		HashSet<String> ips=new HashSet<>();
		
		@ManagedOperation
		@ManagedOperationParameter(name="ip",description = "ip 地址")
		public void setIp(String ip){
			ips.add(ip);
		}
		@ManagedOperation
		@ManagedOperationParameter(name="ip",description = "ip 地址22222")
		public void setIp2(String ip){
			throw new RuntimeException("kjkjkjkjk");
		}

		@ManagedAttribute(description = "获取所有ip啊")
		public String getAll(){
			var all=new StringBuffer();
			for(var s : ips){
				all.append(s);
				all.append(" ");
			}
			return all.toString();
		}
	}

	//切记默认与@EnableWebSocket  冲突使用线程池，好像是websocket aop了默认的线程池，造成sched异常，可以附加一个新池
	@Bean
	public TaskScheduler taskScheduler() {
		ThreadPoolTaskScheduler scheduling = new ThreadPoolTaskScheduler();
		scheduling.setPoolSize(10);
		scheduling.initialize();
		return scheduling;
	}
	@Bean
	ViewResolver createViewResolver(@Autowired ServletContext servletContext) {
		PebbleEngine engine = new PebbleEngine.Builder().autoEscaping(true)
				.cacheActive(false)
				.loader(new ServletLoader(servletContext))
				.extension(new SpringExtension())
				.build();
		PebbleViewResolver viewResolver = new PebbleViewResolver();
		viewResolver.setPrefix("/WEB-INF/templates/");
		viewResolver.setSuffix("");
		viewResolver.setPebbleEngine(engine);
		return viewResolver;
	}

	@Bean
	WebSocketConfigurer createWebSocketConfigurer(
			@Autowired ChatHandler chatHandler,
			@Autowired ChatHandshakeInterceptor chatInterceptor){
		return new WebSocketConfigurer() {
			public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
				// 把URL与指定的WebSocketHandler关联，可关联多个:
				registry.addHandler(chatHandler, "/chat").addInterceptors(chatInterceptor);
			}
		};
	}

	@Bean
	HandlerExceptionResolver getHandlerExceptionResolver(){
		return new HandlerExceptionResolver(){  
			public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex){
				try{
					var pw = response.getWriter();
					pw.write(ex.getMessage()+"----"+ex.getStackTrace());					
					pw.flush();
					ex.printStackTrace();
				}catch(Exception ex2){
					//throw ex2;
				}				
				//不能返回null，否则会抛出异常，返回空mv即可表示已处理了
				return new ModelAndView();
			}
		};
	} 
	// -- Mvc configuration ---------------------------------------------------
	@Bean
	WebMvcConfigurer createWebMvcConfigurer(@Autowired HandlerInterceptor[] interceptors) {
		return new WebMvcConfigurer() {
			@Override
			public void addResourceHandlers(ResourceHandlerRegistry registry) {
				registry.addResourceHandler("/static/**").addResourceLocations("/static/");
			}

			public void addInterceptors(InterceptorRegistry registry) {
				for (var interceptor : interceptors) {
					registry.addInterceptor(interceptor);
				}
			}
			//cros 跨域配置 可设置时间即首次预检查通过后，后续可持续使用的时间
			@Override
			public void addCorsMappings(CorsRegistry registry) {
				registry.addMapping("/api/**")
						.allowedOrigins("http://local.liaoxuefeng.com:8080","http://local.liaoxuefeng.com:8080")
						.allowedMethods("GET", "POST")
						.maxAge(3600);
				// 可以继续添加其他URL规则:
				// registry.addMapping("/rest/v2/**")...
			}
		};
	}
	
	// -- jdbc configuration --------------------------------------------------
	@Value("${jdbc.url}")
	String jdbcUrl;
	@Value("${jdbc.username}")
	String jdbcUsername;
	@Value("${jdbc.password}")
	String jdbcPassword;
	@Bean
	DataSource createDataSource() {
		HikariConfig config = new HikariConfig();
		config.setJdbcUrl(jdbcUrl);
		config.setUsername(jdbcUsername);
		config.setPassword(jdbcPassword);
		config.addDataSourceProperty("autoCommit", "false");
		config.addDataSourceProperty("connectionTimeout", "5");
		config.addDataSourceProperty("idleTimeout", "60");
		return new HikariDataSource(config);
	}
	@Bean
	JdbcTemplate createJdbcTemplate(@Autowired DataSource dataSource) {
		return new JdbcTemplate(dataSource);
	}
	@Bean
	PlatformTransactionManager createTxManager(@Autowired DataSource dataSource) {
		return new DataSourceTransactionManager(dataSource);
	}
}